package com.xing.asm.simple;

import org.objectweb.asm.*;
import org.objectweb.asm.commons.AdviceAdapter;
import org.objectweb.asm.commons.Method;

import java.io.FileOutputStream;

public class AsmSimpleMain {

    public static void main(String[] args) throws Exception{
        ClassReader cr = new ClassReader(AsmSimple.class.getName());
        ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
        ClassVisitor cv = new MethodChangeClassAdapter(cw);
        cr.accept(cv, ClassReader.EXPAND_FRAMES);

        byte[] code = cw.toByteArray();

        FileOutputStream fos = new FileOutputStream("E:\\ljx\\AsmSimple4.class");
        fos.write(code);
        fos.close();

        MyClassLoader myClassLoader = new MyClassLoader();
//        Class clazz = myClassLoader.loadClass("com.xing.asm.simple.PublicApi");
//        clazz.getMethod("hello", null).invoke(clazz.newInstance(), null);

        Class asmSimpleClass = myClassLoader.defineClass(null, code);
        java.lang.reflect.Method method = asmSimpleClass.getMethod("excute", null);
        Object asmSimpleInstance = asmSimpleClass.newInstance();
        method.invoke(asmSimpleInstance);
    }


    public static class MethodChangeClassAdapter extends ClassVisitor implements Opcodes {

        private int localIndex = -1;
        private Type TYPE_PUBLICAPI = Type.getType(PublicApi.class);
        private Type TYPE_VOID = Type.VOID_TYPE;

        public MethodChangeClassAdapter(final ClassVisitor cv) {
            super(Opcodes.ASM5, cv);
        }

        @Override
        public MethodVisitor visitMethod(int access, String name, final String desc, String signature, String[] exceptions) {


            MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
            try {

                if (!"excute".equals(name)) {
                    return mv;
                }

                mv = new AdviceAdapter(Opcodes.ASM5, mv, access, name, desc) {
                    @Override
                    protected void onMethodEnter() {
                        invokeStatic(TYPE_PUBLICAPI, new Method("createPublicApi", TYPE_PUBLICAPI, new Type[0]));

//                        mv.visitVarInsn(ASTORE, 1);
//                        mv.visitVarInsn(ALOAD, 1);

                        localIndex = newLocal(TYPE_PUBLICAPI);
                        storeLocal(localIndex);
                        loadLocal(localIndex);

                        invokeVirtual(TYPE_PUBLICAPI, new Method("hello", TYPE_VOID, new Type[0]));
                    }

                    @Override
                    protected void onMethodExit(int opcode) {
//                      mv.visitVarInsn(ALOAD, 1);

                        if(localIndex != -1) {
                            loadLocal(localIndex);
                        }
                        invokeVirtual(TYPE_PUBLICAPI, new Method("end", TYPE_VOID, new Type[0]));
                    }
                };
            } catch (Exception e) {

            }
            return mv;
        }

    }
}
